
/*-------------------------------------------------------------------------
 *
 * multirangetypes_selfuncs.c
 *	  多范围操作符的选择性估计函数
 *
 * 估计基于下界和上界的直方图，以及空多范围的比例。
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *	  src/backend/utils/adt/multirangetypes_selfuncs.c
 *
 *-------------------------------------------------------------------------
 */
#include "postgres.h"

#include <math.h>

#include "access/htup_details.h"
#include "catalog/pg_operator.h"
#include "catalog/pg_statistic.h"
#include "catalog/pg_type.h"
#include "utils/float.h"
#include "utils/fmgrprotos.h"
#include "utils/lsyscache.h"
#include "utils/rangetypes.h"
#include "utils/multirangetypes.h"
#include "utils/selfuncs.h"
#include "utils/typcache.h"

static double fc_calc_multirangesel(TypeCacheEntry *fc_typcache,
								 VariableStatData *fc_vardata,
								 const MultirangeType *fc_constval, Oid fc_operator);
static double fc_default_multirange_selectivity(Oid fc_operator);
static double fc_calc_hist_selectivity(TypeCacheEntry *fc_typcache,
									VariableStatData *fc_vardata,
									const MultirangeType *fc_constval,
									Oid fc_operator);
static double fc_calc_hist_selectivity_scalar(TypeCacheEntry *fc_typcache,
										   const RangeBound *fc_constbound,
										   const RangeBound *fc_hist,
										   int fc_hist_nvalues, bool fc_equal);
static int	fc_rbound_bsearch(TypeCacheEntry *fc_typcache, const RangeBound *fc_value,
						   const RangeBound *fc_hist, int fc_hist_length, bool fc_equal);
static float8 get_position(TypeCacheEntry *fc_typcache, const RangeBound *fc_value,
						   const RangeBound *fc_hist1, const RangeBound *fc_hist2);
static float8 get_len_position(double fc_value, double fc_hist1, double fc_hist2);
static float8 get_distance(TypeCacheEntry *fc_typcache, const RangeBound *fc_bound1,
						   const RangeBound *fc_bound2);
static int	fc_length_hist_bsearch(Datum *fc_length_hist_values,
								int fc_length_hist_nvalues, double fc_value,
								bool fc_equal);
static double fc_calc_length_hist_frac(Datum *fc_length_hist_values,
									int fc_length_hist_nvalues, double fc_length1,
									double fc_length2, bool fc_equal);
static double fc_calc_hist_selectivity_contained(TypeCacheEntry *fc_typcache,
											  const RangeBound *fc_lower,
											  RangeBound *fc_upper,
											  const RangeBound *fc_hist_lower,
											  int fc_hist_nvalues,
											  Datum *fc_length_hist_values,
											  int fc_length_hist_nvalues);
static double fc_calc_hist_selectivity_contains(TypeCacheEntry *fc_typcache,
											 const RangeBound *fc_lower,
											 const RangeBound *fc_upper,
											 const RangeBound *fc_hist_lower,
											 int fc_hist_nvalues,
											 Datum *fc_length_hist_values,
											 int fc_length_hist_nvalues);

/*
 * 当我们没有统计数据或无法出于某种原因使用它们时，返回给定操作符的默认选择性估计。
 */
static double fc_default_multirange_selectivity(Oid fc_operator)
{
	switch (fc_operator)
	{
		case OID_MULTIRANGE_OVERLAPS_MULTIRANGE_OP:
		case OID_MULTIRANGE_OVERLAPS_RANGE_OP:
		case OID_RANGE_OVERLAPS_MULTIRANGE_OP:
			return 0.01;

		case OID_RANGE_CONTAINS_MULTIRANGE_OP:
		case OID_RANGE_MULTIRANGE_CONTAINED_OP:
		case OID_MULTIRANGE_CONTAINS_RANGE_OP:
		case OID_MULTIRANGE_CONTAINS_MULTIRANGE_OP:
		case OID_MULTIRANGE_RANGE_CONTAINED_OP:
		case OID_MULTIRANGE_MULTIRANGE_CONTAINED_OP:
			return 0.005;

		case OID_MULTIRANGE_CONTAINS_ELEM_OP:
		case OID_MULTIRANGE_ELEM_CONTAINED_OP:

			/*
			 * "multirange @> elem" 与标量不等式 "A >= b AND A <= c" 基本相同。
			 */
			return DEFAULT_MULTIRANGE_INEQ_SEL;

		case OID_MULTIRANGE_LESS_OP:
		case OID_MULTIRANGE_LESS_EQUAL_OP:
		case OID_MULTIRANGE_GREATER_OP:
		case OID_MULTIRANGE_GREATER_EQUAL_OP:
		case OID_MULTIRANGE_LEFT_RANGE_OP:
		case OID_MULTIRANGE_LEFT_MULTIRANGE_OP:
		case OID_RANGE_LEFT_MULTIRANGE_OP:
		case OID_MULTIRANGE_RIGHT_RANGE_OP:
		case OID_MULTIRANGE_RIGHT_MULTIRANGE_OP:
		case OID_RANGE_RIGHT_MULTIRANGE_OP:
		case OID_MULTIRANGE_OVERLAPS_LEFT_RANGE_OP:
		case OID_RANGE_OVERLAPS_LEFT_MULTIRANGE_OP:
		case OID_MULTIRANGE_OVERLAPS_LEFT_MULTIRANGE_OP:
		case OID_MULTIRANGE_OVERLAPS_RIGHT_RANGE_OP:
		case OID_RANGE_OVERLAPS_RIGHT_MULTIRANGE_OP:
		case OID_MULTIRANGE_OVERLAPS_RIGHT_MULTIRANGE_OP:
			/* 这些类似于常规标量不等式 */
			return DEFAULT_INEQ_SEL;

		default:

			/*
			 * 所有多范围运算符应在上述处理，但以防万一
			 */
			return 0.01;
	}
}

/*
 * multirangesel -- 多范围运算符的限制选择性
 */
Datum multirangesel(PG_FUNCTION_ARGS)
{
	PlannerInfo *fc_root = (PlannerInfo *) PG_GETARG_POINTER(0);
	Oid			fc_operator = PG_GETARG_OID(1);
	List	   *fc_args = (List *) PG_GETARG_POINTER(2);
	int			fc_varRelid = PG_GETARG_INT32(3);
	VariableStatData fc_vardata;
	Node	   *fc_other;
	bool		fc_varonleft;
	Selectivity fc_selec;
	TypeCacheEntry *fc_typcache = NULL;
	MultirangeType *fc_constmultirange = NULL;
	RangeType  *fc_constrange = NULL;

	/*
	 * 如果表达式不是 (变量 op 某个值) 或 (某个值 op
	 * 变量)，那么返回默认估计。
	 */
	if (!get_restriction_variable(fc_root, fc_args, fc_varRelid,
								  &fc_vardata, &fc_other, &fc_varonleft))
		PG_RETURN_FLOAT8(fc_default_multirange_selectivity(fc_operator));

	/*
	 * 如果 something 也不是常量，就无法做任何有用的事情。
	 */
	if (!IsA(fc_other, Const))
	{
		ReleaseVariableStats(fc_vardata);
		PG_RETURN_FLOAT8(fc_default_multirange_selectivity(fc_operator));
	}

	/*
	 * 所有多范围运算符都是严格的，因此我们可以直接处理 NULL 常量。
	 */
	if (((Const *) fc_other)->constisnull)
	{
		ReleaseVariableStats(fc_vardata);
		PG_RETURN_FLOAT8(0.0);
	}

	/*
	 * 如果 var 在右侧，则交换运算符，以便我们可以假定 var 在
	 * 接下来的处理过程中在左侧。
	 */
	if (!fc_varonleft)
	{
		/* 我们有其他操作变量，交换以使变量操作其他 */
		fc_operator = get_commutator(fc_operator);
		if (!fc_operator)
		{
			/* 使用默认选择性（我们应该提高错误吗？） */
			ReleaseVariableStats(fc_vardata);
			PG_RETURN_FLOAT8(fc_default_multirange_selectivity(fc_operator));
		}
	}

	/*
	 * 好吧，这里我们处理的是一个 Var 和一个 Const。我们需要常量与列的多范围类型相同，否则我们无法做任何有用的事情。（这种情况可能在运行时失败，但在这里我们宁愿仅返回默认估计。）
	 *
	 * 如果操作符是 "multirange @> 元素"，则常量应为多范围列的元素类型。将其转换为仅包含该单点的多范围，以便我们在接下来的处理中不需要特殊处理。
	 */
	if (fc_operator == OID_MULTIRANGE_CONTAINS_ELEM_OP)
	{
		fc_typcache = multirange_get_typcache(fcinfo, fc_vardata.vartype);

		if (((Const *) fc_other)->consttype == fc_typcache->rngtype->rngelemtype->type_id)
		{
			RangeBound	fc_lower,
						fc_upper;

			fc_lower.inclusive = true;
			fc_lower.val = ((Const *) fc_other)->constvalue;
			fc_lower.infinite = false;
			fc_lower.lower = true;
			fc_upper.inclusive = true;
			fc_upper.val = ((Const *) fc_other)->constvalue;
			fc_upper.infinite = false;
			fc_upper.lower = false;
			fc_constrange = range_serialize(fc_typcache->rngtype, &fc_lower, &fc_upper, false);
			fc_constmultirange = make_multirange(fc_typcache->type_id, fc_typcache->rngtype,
											  1, &fc_constrange);
		}
	}
	else if (fc_operator == OID_RANGE_MULTIRANGE_CONTAINED_OP ||
			 fc_operator == OID_MULTIRANGE_CONTAINS_RANGE_OP ||
			 fc_operator == OID_MULTIRANGE_OVERLAPS_RANGE_OP ||
			 fc_operator == OID_MULTIRANGE_OVERLAPS_LEFT_RANGE_OP ||
			 fc_operator == OID_MULTIRANGE_OVERLAPS_RIGHT_RANGE_OP ||
			 fc_operator == OID_MULTIRANGE_LEFT_RANGE_OP ||
			 fc_operator == OID_MULTIRANGE_RIGHT_RANGE_OP)
	{
		/*
		 * 在 "multirange OP 范围" 中提升范围，就像我们在 "multirange OP 元素" 中对元素所做的那样。
		 */
		fc_typcache = multirange_get_typcache(fcinfo, fc_vardata.vartype);
		if (((Const *) fc_other)->consttype == fc_typcache->rngtype->type_id)
		{
			fc_constrange = DatumGetRangeTypeP(((Const *) fc_other)->constvalue);
			fc_constmultirange = make_multirange(fc_typcache->type_id, fc_typcache->rngtype,
											  1, &fc_constrange);
		}
	}
	else if (fc_operator == OID_RANGE_OVERLAPS_MULTIRANGE_OP ||
			 fc_operator == OID_RANGE_OVERLAPS_LEFT_MULTIRANGE_OP ||
			 fc_operator == OID_RANGE_OVERLAPS_RIGHT_MULTIRANGE_OP ||
			 fc_operator == OID_RANGE_LEFT_MULTIRANGE_OP ||
			 fc_operator == OID_RANGE_RIGHT_MULTIRANGE_OP ||
			 fc_operator == OID_RANGE_CONTAINS_MULTIRANGE_OP ||
			 fc_operator == OID_MULTIRANGE_ELEM_CONTAINED_OP ||
			 fc_operator == OID_MULTIRANGE_RANGE_CONTAINED_OP)
	{
		/*
		 * 在这里，Var 是 elem/range，而不是多范围。目前我们只返回默认估计。在未来，我们可以拆解多范围常量以进行更智能的处理。
		 */
	}
	else if (((Const *) fc_other)->consttype == fc_vardata.vartype)
	{
		/* 两侧都是相同的多范围类型 */
		fc_typcache = multirange_get_typcache(fcinfo, fc_vardata.vartype);

		fc_constmultirange = DatumGetMultirangeTypeP(((Const *) fc_other)->constvalue);
	}

	/*
	 * 如果我们在操作符的一侧得到了有效常量，则继续使用统计数据进行估计。否则返回默认常量估计。注意，calc_multirangesel 不需要处理 OID_MULTIRANGE_*_CONTAINED_OP。
	 */
	if (fc_constmultirange)
		fc_selec = fc_calc_multirangesel(fc_typcache, &fc_vardata, fc_constmultirange, fc_operator);
	else
		fc_selec = fc_default_multirange_selectivity(fc_operator);

	ReleaseVariableStats(fc_vardata);

	CLAMP_PROBABILITY(fc_selec);

	PG_RETURN_FLOAT8((float8) fc_selec);
}

static double fc_calc_multirangesel(TypeCacheEntry *fc_typcache, VariableStatData *fc_vardata,
				   const MultirangeType *fc_constval, Oid fc_operator)
{
	double		fc_hist_selec;
	double		fc_selec;
	float4		fc_empty_frac,
				fc_null_frac;

	/*
	 * 首先查看 pg_statistic 中 NULL 和空多范围的比例。
	 */
	if (HeapTupleIsValid(fc_vardata->statsTuple))
	{
		Form_pg_statistic fc_stats;
		AttStatsSlot fc_sslot;

		fc_stats = (Form_pg_statistic) GETSTRUCT(fc_vardata->statsTuple);
		fc_null_frac = fc_stats->stanullfrac;

		/* 尝试获取空多范围的比例 */
		if (get_attstatsslot(&fc_sslot, fc_vardata->statsTuple,
							 STATISTIC_KIND_RANGE_LENGTH_HISTOGRAM,
							 InvalidOid,
							 ATTSTATSSLOT_NUMBERS))
		{
			if (fc_sslot.nnumbers != 1)
				elog(ERROR, "invalid empty fraction statistic");	/* 不应该发生 */
			fc_empty_frac = fc_sslot.numbers[0];
			free_attstatsslot(&fc_sslot);
		}
		else
		{
			/* 没有空比例统计。假设没有空范围。 */
			fc_empty_frac = 0.0;
		}
	}
	else
	{
		/*
		 * 没有可用的统计数据。无论如何按照下面的计算进行，
		 * 假设没有 NULL 和没有空多范围。这仍然
		 * 使我们能够根据常量是否为空多范围给出一个比什么都没有更好的估计。
		 */
		fc_null_frac = 0.0;
		fc_empty_frac = 0.0;
	}

	if (MultirangeIsEmpty(fc_constval))
	{
		/*
		 * 空多范围匹配所有多范围、所有空多范围，
		 * 或者根据运算符匹配没有任何东西
		 */
		switch (fc_operator)
		{
				/* 如果任一参数为空，则返回 false */
			case OID_MULTIRANGE_OVERLAPS_RANGE_OP:
			case OID_MULTIRANGE_OVERLAPS_MULTIRANGE_OP:
			case OID_MULTIRANGE_OVERLAPS_LEFT_RANGE_OP:
			case OID_MULTIRANGE_OVERLAPS_LEFT_MULTIRANGE_OP:
			case OID_MULTIRANGE_OVERLAPS_RIGHT_RANGE_OP:
			case OID_MULTIRANGE_OVERLAPS_RIGHT_MULTIRANGE_OP:
			case OID_MULTIRANGE_LEFT_RANGE_OP:
			case OID_MULTIRANGE_LEFT_MULTIRANGE_OP:
			case OID_MULTIRANGE_RIGHT_RANGE_OP:
			case OID_MULTIRANGE_RIGHT_MULTIRANGE_OP:
				/* 什么都没有小于空多范围 */
			case OID_MULTIRANGE_LESS_OP:
				fc_selec = 0.0;
				break;

				/*
				 * 只有空多范围可以被空
				 * 多范围包含
				 */
			case OID_RANGE_MULTIRANGE_CONTAINED_OP:
			case OID_MULTIRANGE_MULTIRANGE_CONTAINED_OP:
				/* 只有空范围 <= 空多范围 */
			case OID_MULTIRANGE_LESS_EQUAL_OP:
				fc_selec = fc_empty_frac;
				break;

				/* 一切包含一个空多范围 */
			case OID_MULTIRANGE_CONTAINS_RANGE_OP:
			case OID_MULTIRANGE_CONTAINS_MULTIRANGE_OP:
				/* 一切都是 >= 一个空多范围 */
			case OID_MULTIRANGE_GREATER_EQUAL_OP:
				fc_selec = 1.0;
				break;

				/* 所有非空多范围都是 > 一个空多范围 */
			case OID_MULTIRANGE_GREATER_OP:
				fc_selec = 1.0 - fc_empty_frac;
				break;

				/* 一个元素不能是空的 */
			case OID_MULTIRANGE_CONTAINS_ELEM_OP:

				/* 被 multirangesel() 过滤掉 */
			case OID_RANGE_OVERLAPS_MULTIRANGE_OP:
			case OID_RANGE_OVERLAPS_LEFT_MULTIRANGE_OP:
			case OID_RANGE_OVERLAPS_RIGHT_MULTIRANGE_OP:
			case OID_RANGE_LEFT_MULTIRANGE_OP:
			case OID_RANGE_RIGHT_MULTIRANGE_OP:
			case OID_RANGE_CONTAINS_MULTIRANGE_OP:
			case OID_MULTIRANGE_ELEM_CONTAINED_OP:
			case OID_MULTIRANGE_RANGE_CONTAINED_OP:

			default:
				elog(ERROR, "unexpected operator %u", fc_operator);
				fc_selec = 0.0;	/* 保持编译器安静 */
				break;
		}
	}
	else
	{
		/*
		 * 使用边界直方图计算选择性。如果由于某种原因失败，
		 * 例如 pg_statistic 中没有直方图，则使用
		 * 非空值比例的默认常量估计。这仍然比仅返回默认估计
		 * 稍好，因为这仍然考虑了空和
		 * NULL 元组的比例，如果我们有它们的统计数据。
		 */
		fc_hist_selec = fc_calc_hist_selectivity(fc_typcache, fc_vardata, fc_constval,
										   fc_operator);
		if (fc_hist_selec < 0.0)
			fc_hist_selec = fc_default_multirange_selectivity(fc_operator);

		/*
		 * 现在合并空多范围和直方图
		 * 计算的结果，意识到直方图仅涵盖
		 * 非空、非 NULL 的值。
		 */
		if (fc_operator == OID_RANGE_MULTIRANGE_CONTAINED_OP ||
			fc_operator == OID_MULTIRANGE_MULTIRANGE_CONTAINED_OP)
		{
			/* 空值被任何非空值包含 */
			fc_selec = (1.0 - fc_empty_frac) * fc_hist_selec + fc_empty_frac;
		}
		else
		{
			/* 采用任何其他运算符时，空 Op 非空值不匹配任何东西 */
			fc_selec = (1.0 - fc_empty_frac) * fc_hist_selec;
		}
	}

	/* 所有多范围运算符都是严格的 */
	fc_selec *= (1.0 - fc_null_frac);

	/* 结果应该在范围内，但确保... */
	CLAMP_PROBABILITY(fc_selec);

	return fc_selec;
}

/*
 * 使用多范围边界的直方图计算多范围运算符选择性。
 *
 * 此估计是针对不为空且不
 * NULL 的值部分。
 */
static double fc_calc_hist_selectivity(TypeCacheEntry *fc_typcache, VariableStatData *fc_vardata,
					  const MultirangeType *fc_constval, Oid fc_operator)
{
	TypeCacheEntry *fc_rng_typcache = fc_typcache->rngtype;
	AttStatsSlot fc_hslot;
	AttStatsSlot fc_lslot;
	int			fc_nhist;
	RangeBound *fc_hist_lower;
	RangeBound *fc_hist_upper;
	int			fc_i;
	RangeBound	fc_const_lower;
	RangeBound	fc_const_upper;
	RangeBound	fc_tmp;
	double		fc_hist_selec;

	/* 不能使用不安全的多范围支持函数的直方图 */
	if (!statistic_proc_security_check(fc_vardata,
									   fc_rng_typcache->rng_cmp_proc_finfo.fn_oid))
		return -1;
	if (OidIsValid(fc_rng_typcache->rng_subdiff_finfo.fn_oid) &&
		!statistic_proc_security_check(fc_vardata,
									   fc_rng_typcache->rng_subdiff_finfo.fn_oid))
		return -1;

	/* 尝试获取范围的直方图 */
	if (!(HeapTupleIsValid(fc_vardata->statsTuple) &&
		  get_attstatsslot(&fc_hslot, fc_vardata->statsTuple,
						   STATISTIC_KIND_BOUNDS_HISTOGRAM, InvalidOid,
						   ATTSTATSSLOT_VALUES)))
		return -1.0;

	/* 检查它是否是直方图，而不仅仅是一个虚拟条目 */
	if (fc_hslot.nvalues < 2)
	{
		free_attstatsslot(&fc_hslot);
		return -1.0;
	}

	/*
	 * 将范围的直方图转换为其下界和上界的直方图。
	 */
	fc_nhist = fc_hslot.nvalues;
	fc_hist_lower = (RangeBound *) palloc(sizeof(RangeBound) * fc_nhist);
	fc_hist_upper = (RangeBound *) palloc(sizeof(RangeBound) * fc_nhist);
	for (fc_i = 0; fc_i < fc_nhist; fc_i++)
	{
		bool		fc_empty;

		range_deserialize(fc_rng_typcache, DatumGetRangeTypeP(fc_hslot.values[fc_i]),
						  &fc_hist_lower[fc_i], &fc_hist_upper[fc_i], &fc_empty);
		/* 直方图不应包含任何空范围 */
		if (fc_empty)
			elog(ERROR, "bounds histogram contains an empty range");
	}

	/* @> 和 @< 也需要范围长度的直方图 */
	if (fc_operator == OID_MULTIRANGE_CONTAINS_RANGE_OP ||
		fc_operator == OID_MULTIRANGE_CONTAINS_MULTIRANGE_OP ||
		fc_operator == OID_MULTIRANGE_RANGE_CONTAINED_OP ||
		fc_operator == OID_MULTIRANGE_MULTIRANGE_CONTAINED_OP)
	{
		if (!(HeapTupleIsValid(fc_vardata->statsTuple) &&
			  get_attstatsslot(&fc_lslot, fc_vardata->statsTuple,
							   STATISTIC_KIND_RANGE_LENGTH_HISTOGRAM,
							   InvalidOid,
							   ATTSTATSSLOT_VALUES)))
		{
			free_attstatsslot(&fc_hslot);
			return -1.0;
		}

		/* 检查它是否是直方图，而不仅仅是一个虚拟条目 */
		if (fc_lslot.nvalues < 2)
		{
			free_attstatsslot(&fc_lslot);
			free_attstatsslot(&fc_hslot);
			return -1.0;
		}
	}
	else
		memset(&fc_lslot, 0, sizeof(fc_lslot));

	/* 提取常量值的边界。 */
	Assert(fc_constval->rangeCount > 0);
	multirange_get_bounds(fc_rng_typcache, fc_constval, 0,
						  &fc_const_lower, &fc_tmp);
	multirange_get_bounds(fc_rng_typcache, fc_constval, fc_constval->rangeCount - 1,
						  &fc_tmp, &fc_const_upper);

	/*
	 * 计算选择性，将常量的下界或上界与
	 * 下界或上界的直方图进行比较。
	 */
	switch (fc_operator)
	{
		case OID_MULTIRANGE_LESS_OP:

			/*
			 * 常规 B 树比较运算符 (<, <=, >, >=) 首先比较
			 * 下界，对于下界相等的值再比较上界。只通过比较下界来进行估计。
			 * 假设没有许多行的下界等于常量的
			 * 下界，这给出了相当准确的估计。
			 */
			fc_hist_selec =
				fc_calc_hist_selectivity_scalar(fc_rng_typcache, &fc_const_lower,
											 fc_hist_lower, fc_nhist, false);
			break;

		case OID_MULTIRANGE_LESS_EQUAL_OP:
			fc_hist_selec =
				fc_calc_hist_selectivity_scalar(fc_rng_typcache, &fc_const_lower,
											 fc_hist_lower, fc_nhist, true);
			break;

		case OID_MULTIRANGE_GREATER_OP:
			fc_hist_selec =
				1 - fc_calc_hist_selectivity_scalar(fc_rng_typcache, &fc_const_lower,
												 fc_hist_lower, fc_nhist, false);
			break;

		case OID_MULTIRANGE_GREATER_EQUAL_OP:
			fc_hist_selec =
				1 - fc_calc_hist_selectivity_scalar(fc_rng_typcache, &fc_const_lower,
												 fc_hist_lower, fc_nhist, true);
			break;

		case OID_MULTIRANGE_LEFT_RANGE_OP:
		case OID_MULTIRANGE_LEFT_MULTIRANGE_OP:
			/* var << const 当 upper(var) < lower(const) 时 */
			fc_hist_selec =
				fc_calc_hist_selectivity_scalar(fc_rng_typcache, &fc_const_lower,
											 fc_hist_upper, fc_nhist, false);
			break;

		case OID_MULTIRANGE_RIGHT_RANGE_OP:
		case OID_MULTIRANGE_RIGHT_MULTIRANGE_OP:
			/* var >> const 当 lower(var) > upper(const) */
			fc_hist_selec =
				1 - fc_calc_hist_selectivity_scalar(fc_rng_typcache, &fc_const_upper,
												 fc_hist_lower, fc_nhist, true);
			break;

		case OID_MULTIRANGE_OVERLAPS_RIGHT_RANGE_OP:
		case OID_MULTIRANGE_OVERLAPS_RIGHT_MULTIRANGE_OP:
			/* 比较下界 */
			fc_hist_selec =
				1 - fc_calc_hist_selectivity_scalar(fc_rng_typcache, &fc_const_lower,
												 fc_hist_lower, fc_nhist, false);
			break;

		case OID_MULTIRANGE_OVERLAPS_LEFT_RANGE_OP:
		case OID_MULTIRANGE_OVERLAPS_LEFT_MULTIRANGE_OP:
			/* 比较上界 */
			fc_hist_selec =
				fc_calc_hist_selectivity_scalar(fc_rng_typcache, &fc_const_upper,
											 fc_hist_upper, fc_nhist, true);
			break;

		case OID_MULTIRANGE_OVERLAPS_RANGE_OP:
		case OID_MULTIRANGE_OVERLAPS_MULTIRANGE_OP:
		case OID_MULTIRANGE_CONTAINS_ELEM_OP:

			/*
			 * A && B <=> NOT (A << B OR A >> B).
			 *
			 * 由于 A << B 和 A >> B 是互斥事件，我们可以
			 * 将它们的概率相加来找出 (A << B OR A >>
			 * B) 的概率。
			 *
			 * "multirange @> elem" 相当于 "multirange &&
			 * {[elem,elem]}". 调用者已经从元素常量构造了单一
			 * 范围，因此只需将其视为
			 * &&。
			 */
			fc_hist_selec =
				fc_calc_hist_selectivity_scalar(fc_rng_typcache,
											 &fc_const_lower, fc_hist_upper,
											 fc_nhist, false);
			fc_hist_selec +=
				(1.0 - fc_calc_hist_selectivity_scalar(fc_rng_typcache,
													&fc_const_upper, fc_hist_lower,
													fc_nhist, true));
			fc_hist_selec = 1.0 - fc_hist_selec;
			break;

		case OID_MULTIRANGE_CONTAINS_RANGE_OP:
		case OID_MULTIRANGE_CONTAINS_MULTIRANGE_OP:
			fc_hist_selec =
				fc_calc_hist_selectivity_contains(fc_rng_typcache, &fc_const_lower,
											   &fc_const_upper, fc_hist_lower, fc_nhist,
											   fc_lslot.values, fc_lslot.nvalues);
			break;

		case OID_MULTIRANGE_MULTIRANGE_CONTAINED_OP:
		case OID_RANGE_MULTIRANGE_CONTAINED_OP:
			if (fc_const_lower.infinite)
			{
				/*
				 * 下界不再重要。只需估算一个上界 <= const 上界
				 */
				fc_hist_selec =
					fc_calc_hist_selectivity_scalar(fc_rng_typcache, &fc_const_upper,
												 fc_hist_upper, fc_nhist, true);
			}
			else if (fc_const_upper.infinite)
			{
				fc_hist_selec =
					1.0 - fc_calc_hist_selectivity_scalar(fc_rng_typcache, &fc_const_lower,
													   fc_hist_lower, fc_nhist, false);
			}
			else
			{
				fc_hist_selec =
					fc_calc_hist_selectivity_contained(fc_rng_typcache, &fc_const_lower,
													&fc_const_upper, fc_hist_lower, fc_nhist,
													fc_lslot.values, fc_lslot.nvalues);
			}
			break;

			/* 被 multirangesel() 过滤掉 */
		case OID_RANGE_OVERLAPS_MULTIRANGE_OP:
		case OID_RANGE_OVERLAPS_LEFT_MULTIRANGE_OP:
		case OID_RANGE_OVERLAPS_RIGHT_MULTIRANGE_OP:
		case OID_RANGE_LEFT_MULTIRANGE_OP:
		case OID_RANGE_RIGHT_MULTIRANGE_OP:
		case OID_RANGE_CONTAINS_MULTIRANGE_OP:
		case OID_MULTIRANGE_ELEM_CONTAINED_OP:
		case OID_MULTIRANGE_RANGE_CONTAINED_OP:

		default:
			elog(ERROR, "unknown multirange operator %u", fc_operator);
			fc_hist_selec = -1.0;	/* 保持编译器安静 */
			break;
	}

	free_attstatsslot(&fc_lslot);
	free_attstatsslot(&fc_hslot);

	return fc_hist_selec;
}


/*
 * 在范围边界的直方图中查找小于（或等于，如果 'equal' 参数
 * 为真）给定常量的值的比例。
 */
static double fc_calc_hist_selectivity_scalar(TypeCacheEntry *fc_typcache, const RangeBound *fc_constbound,
							 const RangeBound *fc_hist, int fc_hist_nvalues, bool fc_equal)
{
	Selectivity fc_selec;
	int			fc_index;

	/*
	 * 找出给定常量所处的直方图箱。估计选取率为
	 * 前面完整箱的数量。
	 */
	fc_index = fc_rbound_bsearch(fc_typcache, fc_constbound, fc_hist, fc_hist_nvalues, fc_equal);
	fc_selec = (Selectivity) (Max(fc_index, 0)) / (Selectivity) (fc_hist_nvalues - 1);

	/* 使用线性插值调整箱内的值 */
	if (fc_index >= 0 && fc_index < fc_hist_nvalues - 1)
		fc_selec += get_position(fc_typcache, fc_constbound, &fc_hist[fc_index],
							  &fc_hist[fc_index + 1]) / (Selectivity) (fc_hist_nvalues - 1);

	return fc_selec;
}

/*
 * 在范围边界数组上进行二分查找。返回小于（小于或等于）给定范围边界的范围
 * 边界的最大索引。如果数组中的所有范围边界都大于或等于（大于）给定范围
 * 边界，则返回 -1。当 "equal" 标志被设置时，
 * 使用括号中的条件。
 *
 * 此函数用于标量操作符的选取率估计。此函数的另一个
 * 目标是找出停止插值的直方图箱，插值的部分边界小于或等于给定边界。
 */
static int fc_rbound_bsearch(TypeCacheEntry *fc_typcache, const RangeBound *fc_value, const RangeBound *fc_hist,
			   int fc_hist_length, bool fc_equal)
{
	int			fc_lower = -1,
				fc_upper = fc_hist_length - 1,
				fc_cmp,
				fc_middle;

	while (fc_lower < fc_upper)
	{
		fc_middle = (fc_lower + fc_upper + 1) / 2;
		fc_cmp = range_cmp_bounds(fc_typcache, &fc_hist[fc_middle], fc_value);

		if (fc_cmp < 0 || (fc_equal && fc_cmp == 0))
			fc_lower = fc_middle;
		else
			fc_upper = fc_middle - 1;
	}
	return fc_lower;
}


/*
 * 在长度直方图上进行二分查找。返回小于（小于或等于）给定长度值的
 * 直方图中范围长度的最大索引。如果直方图中的所有长度大于（大于或等于）
 * 给定长度，则返回 -1。
 */
static int fc_length_hist_bsearch(Datum *fc_length_hist_values, int fc_length_hist_nvalues,
					double fc_value, bool fc_equal)
{
	int			fc_lower = -1,
				fc_upper = fc_length_hist_nvalues - 1,
				fc_middle;

	while (fc_lower < fc_upper)
	{
		double		fc_middleval;

		fc_middle = (fc_lower + fc_upper + 1) / 2;

		fc_middleval = DatumGetFloat8(fc_length_hist_values[fc_middle]);
		if (fc_middleval < fc_value || (fc_equal && fc_middleval <= fc_value))
			fc_lower = fc_middle;
		else
			fc_upper = fc_middle - 1;
	}
	return fc_lower;
}

/*
 * 获取直方图箱中值的相对位置，范围在 [0,1]。
 */
static float8 get_position(TypeCacheEntry *fc_typcache, const RangeBound *fc_value, const RangeBound *fc_hist1,
			 const RangeBound *fc_hist2)
{
	bool		fc_has_subdiff = OidIsValid(fc_typcache->rng_subdiff_finfo.fn_oid);
	float8		fc_position;

	if (!fc_hist1->infinite && !fc_hist2->infinite)
	{
		float8		fc_bin_width;

		/*
		 * 两个边界都是有限的。假设子类型的比较函数正常工作，
		 * 值也必须是有限的，因为它位于边界之间的某个位置。
		 * 如果不是，则任意返回 0.5。
		 */
		if (fc_value->infinite)
			return 0.5;

		/* 没有 subdiff 函数不能插值 */
		if (!fc_has_subdiff)
			return 0.5;

		/* 使用 subdiff 函数计算相对位置。 */
		fc_bin_width = DatumGetFloat8(FunctionCall2Coll(&fc_typcache->rng_subdiff_finfo,
													 fc_typcache->rng_collation,
													 fc_hist2->val,
													 fc_hist1->val));
		if (isnan(fc_bin_width) || fc_bin_width <= 0.0)
			return 0.5;			/* 对于 NaN 或零宽度箱，直接返回 */

		fc_position = DatumGetFloat8(FunctionCall2Coll(&fc_typcache->rng_subdiff_finfo,
													fc_typcache->rng_collation,
													fc_value->val,
													fc_hist1->val))
			/ fc_bin_width;

		if (isnan(fc_position))
			return 0.5;			/* 对于来自 subdiff 的 NaN，Inf/Inf 等，直接返回 */

		/* 相对位置必须在 [0,1] 范围内 */
		fc_position = Max(fc_position, 0.0);
		fc_position = Min(fc_position, 1.0);
		return fc_position;
	}
	else if (fc_hist1->infinite && !fc_hist2->infinite)
	{
		/*
		 * 下箱边界为 -无穷，上箱边界为有限。如果值为
		 * -无穷，则返回 0.0 表示它等于下边界。
		 * 否则返回 1.0 表示它与下边界之间是无限远的。
		 */
		return ((fc_value->infinite && fc_value->lower) ? 0.0 : 1.0);
	}
	else if (!fc_hist1->infinite && fc_hist2->infinite)
	{
		/* 同上，但反向 */
		return ((fc_value->infinite && !fc_value->lower) ? 1.0 : 0.0);
	}
	else
	{
		/*
		 * 如果两个箱边界都是无穷大，它们应该相等，
		 * 值也应该是无穷大，并且等于两个边界。
		 * （但不要断言，以避免崩溃，如果用户创建了一个
		 * 带有损坏比较函数的数据类型）。
		 *
		 * 假设值位于无穷边界的中间。
		 */
		return 0.5;
	}
}


/*
 * 获取值在长度直方图箱中的相对位置，范围在 [0,1]。
 */
static double get_len_position(double fc_value, double fc_hist1, double fc_hist2)
{
	if (!isinf(fc_hist1) && !isinf(fc_hist2))
	{
		/*
		 * 两个边界都是有限的。值也应该是有限的，因为它
		 * 位于边界之间的某个位置。如果不是，就返回
		 * 某个值。
		 */
		if (isinf(fc_value))
			return 0.5;

		return 1.0 - (fc_hist2 - fc_value) / (fc_hist2 - fc_hist1);
	}
	else if (isinf(fc_hist1) && !isinf(fc_hist2))
	{
		/*
		 * 下箱边界为 -无穷，上箱边界为有限。返回 1.0 表示
		 * 值与下边界之间是无限远的。
		 */
		return 1.0;
	}
	else if (isinf(fc_hist1) && isinf(fc_hist2))
	{
		/* 同上，但反向 */
		return 0.0;
	}
	else
	{
		/*
		 * 如果两个箱边界都是无穷大，它们应该相等，
		 * 值也应该是无穷大，并且等于两个边界。
		 * （但不要断言，以避免不必要的崩溃，如果
		 * 调用者搞砸了）
		 *
		 * 假设值位于无穷边界的中间。
		 */
		return 0.5;
	}
}

/*
 * 测量两个范围边界之间的距离。
 */
static float8 get_distance(TypeCacheEntry *fc_typcache, const RangeBound *fc_bound1, const RangeBound *fc_bound2)
{
	bool		fc_has_subdiff = OidIsValid(fc_typcache->rng_subdiff_finfo.fn_oid);

	if (!fc_bound1->infinite && !fc_bound2->infinite)
	{
		/*
		 * 两个边界都不是无穷大，使用 subdiff 函数，或者如果没有
		 * subdiff 可用，则返回默认值 1.0。
		 */
		if (fc_has_subdiff)
		{
			float8		fc_res;

			fc_res = DatumGetFloat8(FunctionCall2Coll(&fc_typcache->rng_subdiff_finfo,
												   fc_typcache->rng_collation,
												   fc_bound2->val,
												   fc_bound1->val));
			/* 拒绝可能的 NaN 结果，也拒绝负结果 */
			if (isnan(fc_res) || fc_res < 0.0)
				return 1.0;
			else
				return fc_res;
		}
		else
			return 1.0;
	}
	else if (fc_bound1->infinite && fc_bound2->infinite)
	{
		/* 两个边界都是无穷大 */
		if (fc_bound1->lower == fc_bound2->lower)
			return 0.0;
		else
			return get_float8_infinity();
	}
	else
	{
		/* 一个边界是无穷大，另一个不是 */
		return get_float8_infinity();
	}
}

/*
 * 在区间 [length1, length2] 中计算函数 P(x) 的平均值，
 * 其中 P(x) 是长度 < x 的元组的比例（如果 'equal' 为真，则为长度 <= x）。
 */
static double fc_calc_length_hist_frac(Datum *fc_length_hist_values, int fc_length_hist_nvalues,
					  double fc_length1, double fc_length2, bool fc_equal)
{
	double		fc_frac;
	double		fc_A,
				fc_B,
				fc_PA,
				fc_PB;
	double		fc_pos;
	int			fc_i;
	double		fc_area;

	Assert(fc_length2 >= fc_length1);

	if (fc_length2 < 0.0)
		return 0.0;				/* 不应该发生，但检查一下也没坏处 */

	/* 表中的所有长度都 <= 无限。 */
	if (isinf(fc_length2) && fc_equal)
		return 1.0;

	/*----------
	 * 函数在 A 和 B 之间的平均值可以通过以下公式计算：
	 *
	 *			B
	 *	  1		/
	 * -------	| P(x)dx
	 *	B - A	/
	 *			A
	 *
	 * 积分的几何解释是 P(x) 图形下的面积。P(x) 由长度直方图定义。我们通过逐段计算面积，遍历长度直方图的每个区间。每个区间是一个梯形：
	 *
	 *		 P(x2)
	 *		  /|
	 *		 / |
	 * P(x1)/  |
	 *	   |   |
	 *	   |   |
	 *	---+---+--
	 *	   x1  x2
	 *
	 * 其中 x1 和 x2 是当前直方图的边界，P(x1) 和 P(x2) 是边界处元组的累积分数。
	 *
	 * 每个梯形的面积是 1/2 * (P(x2) + P(x1)) * (x2 - x1)
	 *
	 * 第一个区间包含调用者传递的下限，因此我们在前一个和下一个直方图区间边界之间使用线性插值来计算 P(x1)。对于最后一个区间也是如此：我们使用线性插值来计算 P(x2)。对于中间的区间，x1 和 x2 位于直方图区间边界上，因此 P(x1) 和 P(x2) 简单计算为：
	 * P(x1) =	  (区间索引) / (区间总数)
	 * P(x2) = (区间索引 + 1) / (区间总数)
	 */

	/* 第一个区间，包含下限 */
	fc_i = fc_length_hist_bsearch(fc_length_hist_values, fc_length_hist_nvalues, fc_length1, fc_equal);
	if (fc_i >= fc_length_hist_nvalues - 1)
		return 1.0;

	if (fc_i < 0)
	{
		fc_i = 0;
		fc_pos = 0.0;
	}
	else
	{
		/* 插值 length1 在该区间的位置 */
		fc_pos = get_len_position(fc_length1,
							   DatumGetFloat8(fc_length_hist_values[fc_i]),
							   DatumGetFloat8(fc_length_hist_values[fc_i + 1]));
	}
	fc_PB = (((double) fc_i) + fc_pos) / (double) (fc_length_hist_nvalues - 1);
	fc_B = fc_length1;

	/*
	 * 在 degenerate 情况下 length1 == length2，简单返回
	 * P(length1)。这不仅仅是优化：如果 length1 == length2,
	 * 我们稍后会出现除以零的情况。
	 */
	if (fc_length2 == fc_length1)
		return fc_PB;

	/*
	 * 遍历所有区间，直到我们到达最后一个区间，也就是包含上限的那个。
	 * （如果下限和上限在同一个区间内，这会立即出现）
	 */
	fc_area = 0.0;
	for (; fc_i < fc_length_hist_nvalues - 1; fc_i++)
	{
		double		fc_bin_upper = DatumGetFloat8(fc_length_hist_values[fc_i + 1]);

		/* 检查我们是否到达最后一个区间 */
		if (!(fc_bin_upper < fc_length2 || (fc_equal && fc_bin_upper <= fc_length2)))
			break;

		/* 上一个区间的上限是当前区间的下限 */
		fc_A = fc_B;
		fc_PA = fc_PB;

		fc_B = fc_bin_upper;
		fc_PB = (double) fc_i / (double) (fc_length_hist_nvalues - 1);

		/*
		 * 将这个梯形的面积加到总面积中。if 检查的目的是避免 NaN，
		 * 在 PA == PB == 0 的极端情况下，B - A == Inf。零高度的梯形面积（PA == PB ==
		 * 0）为零，无论宽度如何（B - A）。
		 */
		if (fc_PA > 0 || fc_PB > 0)
			fc_area += 0.5 * (fc_PB + fc_PA) * (fc_B - fc_A);
	}

	/* Last bin */
	fc_A = fc_B;
	fc_PA = fc_PB;

	fc_B = fc_length2;				/* 最后一个区间以查询上限结束 */
	if (fc_i >= fc_length_hist_nvalues - 1)
		fc_pos = 0.0;
	else
	{
		if (DatumGetFloat8(fc_length_hist_values[fc_i]) == DatumGetFloat8(fc_length_hist_values[fc_i + 1]))
			fc_pos = 0.0;
		else
			fc_pos = get_len_position(fc_length2,
								   DatumGetFloat8(fc_length_hist_values[fc_i]),
								   DatumGetFloat8(fc_length_hist_values[fc_i + 1]));
	}
	fc_PB = (((double) fc_i) + fc_pos) / (double) (fc_length_hist_nvalues - 1);

	if (fc_PA > 0 || fc_PB > 0)
		fc_area += 0.5 * (fc_PB + fc_PA) * (fc_B - fc_A);

	/*
	 * 好吧，我们已经计算了面积，即积分。除以宽度以获取
	 * 请求的平均值。
	 *
	 * 避免因无限 / 无限而产生的 NaN。至少在 length2 是无限的情况下会发生。
	 * 在这种情况下，不清楚正确的值是什么，所以 0.5 似乎和其他值一样好。
	 */
	if (isinf(fc_area) && isinf(fc_length2))
		fc_frac = 0.5;
	else
		fc_frac = fc_area / (fc_length2 - fc_length1);

	return fc_frac;
}

/*
 * 计算 "var <@ const" 操作符的选择性，即估计属于常量下限和上限范围的多范围的百分比。
 * 这使用范围下限和范围长度的直方图，假设范围长度独立于下限。
 *
 * 调用者已经检查过常量下限和上限是有限的。
 */
static double fc_calc_hist_selectivity_contained(TypeCacheEntry *fc_typcache,
								const RangeBound *fc_lower, RangeBound *fc_upper,
								const RangeBound *fc_hist_lower, int fc_hist_nvalues,
								Datum *fc_length_hist_values, int fc_length_hist_nvalues)
{
	int			fc_i,
				fc_upper_index;
	float8		fc_prev_dist;
	double		fc_bin_width;
	double		fc_upper_bin_width;
	double		fc_sum_frac;

	/*
	 * 首先在下限直方图中查找包含上限的区间。
	 * 任何下限大于常量上限的范围都不能匹配，即在大于 upper_index 的区间中没有匹配。
	 */
	fc_upper->inclusive = !fc_upper->inclusive;
	fc_upper->lower = true;
	fc_upper_index = fc_rbound_bsearch(fc_typcache, fc_upper, fc_hist_lower, fc_hist_nvalues,
								 false);

	/*
	 * 如果上限值低于直方图的下限，则没有匹配项。
	 */
	if (fc_upper_index < 0)
		return 0.0;

	/*
	 * 如果上限值在直方图的上限内或超过上限，则从最后一个实际区间开始我们的循环，
	 * 就好像上限在那个区间内；无论如何 get_position 将其结果限制在 1.0。
	 * （这对应于假定直方图的上限以上的数据人口为空，正如我们刚刚对下限所做的假设。）
	 */
	fc_upper_index = Min(fc_upper_index, fc_hist_nvalues - 2);

	/*
	 * 计算 upper_bin_width，即 (upper_index, upper_index + 1) 区间中大于查询范围上限的部分，
	 * 使用 subdiff 函数的线性插值。
	 */
	fc_upper_bin_width = get_position(fc_typcache, fc_upper,
								   &fc_hist_lower[fc_upper_index],
								   &fc_hist_lower[fc_upper_index + 1]);

	/*
	 * 在循环中，dist 和 prev_dist 是 "当前" 区间的
	 * 下限和上限距离常量上限的距离。
	 *
	 * bin_width 表示当前区间的宽度。通常它是 1.0，
	 * 表示一个完整的宽度区间，但在极端情况下可以更小：循环的开始
	 * 和结束。我们以 bin_width = upper_bin_width 开始，因为
	 * 我们从包含上限的区间开始。
	 */
	fc_prev_dist = 0.0;
	fc_bin_width = fc_upper_bin_width;

	fc_sum_frac = 0.0;
	for (fc_i = fc_upper_index; fc_i >= 0; fc_i--)
	{
		double		fc_dist;
		double		fc_length_hist_frac;
		bool		fc_final_bin = false;

		/*
		 * dist -- 查询范围的上限到当前区间的下限在
		 * 下限直方图中的距离。或者在最后一个区间内，是到
		 * 常量范围的下限的距离，如果该区间包含常量下限。
		 */
		if (range_cmp_bounds(fc_typcache, &fc_hist_lower[fc_i], fc_lower) < 0)
		{
			fc_dist = get_distance(fc_typcache, fc_lower, fc_upper);

			/*
			 * 从 bin_width 中减去我们想要忽略的这一部分。
			 */
			fc_bin_width -= get_position(fc_typcache, fc_lower, &fc_hist_lower[fc_i],
									  &fc_hist_lower[fc_i + 1]);
			if (fc_bin_width < 0.0)
				fc_bin_width = 0.0;
			fc_final_bin = true;
		}
		else
			fc_dist = get_distance(fc_typcache, &fc_hist_lower[fc_i], fc_upper);

		/*
		 * 估计该区间中元组的百分比，这些元组的宽度足够小，
		 * 不会超过查询范围上限的距离。
		 */
		fc_length_hist_frac = fc_calc_length_hist_frac(fc_length_hist_values,
												 fc_length_hist_nvalues,
												 fc_prev_dist, fc_dist, true);

		
/*
		 * 将这个区间中适当长度的元组比例添加到总数中。
		 */
		fc_sum_frac += fc_length_hist_frac * fc_bin_width / (double) (fc_hist_nvalues - 1);

		if (fc_final_bin)
			break;

		fc_bin_width = 1.0;
		fc_prev_dist = fc_dist;
	}

	return fc_sum_frac;
}

/*
 * 计算“var @> const”操作符的选择性，即估计包含常量下限和上限的多范围的比例。这使用范围下限和范围长度的直方图，假设范围长度与下限是独立的。
 */
static double fc_calc_hist_selectivity_contains(TypeCacheEntry *fc_typcache,
							   const RangeBound *fc_lower, const RangeBound *fc_upper,
							   const RangeBound *fc_hist_lower, int fc_hist_nvalues,
							   Datum *fc_length_hist_values, int fc_length_hist_nvalues)
{
	int			fc_i,
				fc_lower_index;
	double		fc_bin_width,
				fc_lower_bin_width;
	double		fc_sum_frac;
	float8		fc_prev_dist;

	/* 找到包含查询范围下限的区间。 */
	fc_lower_index = fc_rbound_bsearch(fc_typcache, fc_lower, fc_hist_lower, fc_hist_nvalues,
								 true);

	/*
	 * 如果下限值低于直方图的下限，则没有匹配项。
	 */
	if (fc_lower_index < 0)
		return 0.0;

	/*
	 * 如果下限值在直方图的上限或更高，开始我们的循环在最后一个实际区间，就像上限在该区间内一样；get_position会将其结果限制在1.0以内。
	 * （这相当于假设直方图上限以上的数据是空的，正如我们刚才对下限所假设的那样。）
	 */
	fc_lower_index = Min(fc_lower_index, fc_hist_nvalues - 2);

	/*
	 * 计算lower_bin_width，即（lower_index，lower_index + 1）区间中大于查询范围下限的比例，使用subdiff函数的线性插值法。
	 */
	fc_lower_bin_width = get_position(fc_typcache, fc_lower, &fc_hist_lower[fc_lower_index],
								   &fc_hist_lower[fc_lower_index + 1]);

	/*
	 * 遍历所有小于查询下限的下限区间。在循环中，dist和prev_dist是“当前”区间的下限和上限到常量上限的距离。
	 * 我们从查询下限开始，向后走，因此第一个区间的上限是查询下限，它与查询上限的距离是查询范围的长度。
	 *
	 * bin_width表示当前区间的宽度。通常为1.0，意味着完整宽度的区间，除了第一个区间，只计入到常量下限。
	 */
	fc_prev_dist = get_distance(fc_typcache, fc_lower, fc_upper);
	fc_sum_frac = 0.0;
	fc_bin_width = fc_lower_bin_width;
	for (fc_i = fc_lower_index; fc_i >= 0; fc_i--)
	{
		float8		fc_dist;
		double		fc_length_hist_frac;

		/*
		 * dist -- 查询范围上限与当前下限直方图值或查询范围下限（如果我们已经到达）的距离。
		 */
		fc_dist = get_distance(fc_typcache, &fc_hist_lower[fc_i], fc_upper);

		/*
		 * 获取覆盖时间间隔长于（或等于）到查询范围上限距离的长度直方图的平均比例。
		 */
		fc_length_hist_frac =
			1.0 - fc_calc_length_hist_frac(fc_length_hist_values,
										fc_length_hist_nvalues,
										fc_prev_dist, fc_dist, false);

		fc_sum_frac += fc_length_hist_frac * fc_bin_width / (double) (fc_hist_nvalues - 1);

		fc_bin_width = 1.0;
		fc_prev_dist = fc_dist;
	}

	return fc_sum_frac;
}
